Skip to content

feat: production-grade escrow lifecycle hardening#4

Merged
natalya-bbr merged 18 commits intomainfrom
develop
Dec 11, 2025
Merged

feat: production-grade escrow lifecycle hardening#4
natalya-bbr merged 18 commits intomainfrom
develop

Conversation

@natalya-bbr
Copy link
Copy Markdown
Owner

Implements: Escrow lifecycle hardening to production-grade standards with RulesEngine integration, comprehensive testing, and successful Base deployment.

Changes

Core Features

  • RulesEngine Integration: SafeBaseEscrow now supports programmable condition-based releases/refunds via ruleSetId
  • Disputed → Released Transition: Mediators can now resolve disputes in seller's favor
  • Security Hardening: Enforced mediator-only actions in Disputed state to prevent dispute bypass

Add comprehensive tests for SafeBaseEscrowV1 covering:
- Proxy initialization
- Escrow creation with validation
- State transitions
- Pause/unpause functionality
- Upgrade authorization
…d Treasury

- RulesEngineV1: test rule creation, validation, and conditional logic
- RegistryV1: test escrow indexing, pagination, and party queries
- ExecutorV1: test automation, deadline checks, and access control
- Treasury: test multi-sig withdrawal flow, admin management, and ERC20 support
- Update foundry.toml with gas reports and fuzz configuration
- Remove incomplete workflow files (deps.yml, slither-analysis.yml)
- Update CI to run on both main and develop branches
- Add gas snapshot tracking with 5% tolerance
- Add contract size reporting in build step
- Add coverage report with lcov format
- Add Codecov integration for coverage tracking
- Change ETH transfer from transfer() to call{value}()
- transfer() has 2300 gas limit which fails with UUPS proxies
- call() forwards all available gas allowing proxy fallback to execute
- Add error handling for failed transfers
- Fixes 17 failing tests in SafeBaseEscrowV1Test

Resolves OutOfGas errors when funding escrow with ETH to Treasury proxy.
- Add treasury.executeWithdrawal() after approveWithdrawal()
- Ensures funds are immediately transferred to recipient
- releaseToSeller now completes full withdrawal flow
- refundToBuyer now completes full withdrawal flow
- Fixes 4 failing balance assertion tests

This completes the withdrawal process instead of just approving it,
since SafeBaseEscrow has both admin and executor roles in Treasury.
- Add conditional check for .gas-snapshot file existence
- Create snapshot on first run instead of checking
- Check snapshot with 5% tolerance on subsequent runs
- Prevents CI failure when snapshot file doesn't exist

Fixes "No such file or directory" error in GitHub Actions.
- Add UpgradeContract.s.sol for upgrading any UUPS contract
- Add GitHub Actions workflow for manual contract upgrades
- Supports all contracts: Treasury, SafeBaseEscrow, RulesEngine, etc.
- Choose network (sepolia/mainnet) and contract via UI
- Includes dry run simulation before actual upgrade
- Auto-verifies new implementation on Basescan

Usage: GitHub Actions -> Upgrade Contract -> Run workflow
Base Mainnet (8453):
- Old: 0x4b015dF938d053388796078FA3F5145C550C4EE8
- New: 0x57741EE5bAc991D43Cf71207781fCB0eE4b9e9a8
- Verified: https://basescan.org/address/0x57741ee5bac991d43cf71207781fcb0ee4b9e9a8

Base Sepolia (84532):
- Old: 0x9b0C3872234e403aa62F7315122019c22D9e4F11
- New: 0xF2a7fbffD5760721C99104A7C1f500797F3D1314
- Verified: https://sepolia.basescan.org/address/0xf2a7fbffd5760721c99104a7c1f500797f3d1314

Changes in upgraded implementation:
- Fixed OutOfGas issue with ETH transfers to Treasury proxy
- Added executeWithdrawal calls for immediate fund transfers
- All 85 tests passing
In OZ v4.9.6, __Ownable_init() takes no parameters. Use
_transferOwnership() to set custom owner after initialization.

Fixes initialization in:
- SafeBaseEscrowV1
- RulesEngineV1
Added comprehensive tests for mediator-driven resolution of disputed
escrows:
- testDisputedToReleased: mediator releases funds after dispute
- testDisputedToReleasedRequiresMediator: buyer cannot release from Disputed
- testMultipleDisputeAttempts: prevents double-dispute
- testReleaseAfterDeadlineWithApproval: approval works post-deadline
- testCannotReleaseFromCancelledState: terminal state enforcement
- testCannotRefundFromCancelledState: terminal state enforcement

Covers edge cases identified in lifecycle hardening audit.
Created SafeBaseEscrowRulesIntegration.t.sol covering:
- Release with rules-based approval logic
- Refund with deadline-based rules
- Mediator override capabilities
- Disputed state resolution with rules
- Backward compatibility (ruleSetId = 0)

Validates complete integration between SafeBaseEscrowV1 and
RulesEngineV1 across all state transitions.
Comprehensive documentation for SafeBase escrow state machine:

- 6 states with complete transition diagram
- Authorization matrix for each role
- RulesEngine integration patterns
- Invariants (state, financial, temporal)
- Edge cases and race conditions
- Security model and attack vectors
- Integration examples and upgrade guide

Serves as reference for developers integrating with SafeBase
and auditors reviewing lifecycle logic.
OpenZeppelin v5.x requires initialOwner parameter in __Ownable_init().
Updated initialization to use __Ownable_init(_owner) instead of
parameterless init followed by transferOwnership.

Fixes compilation errors in SafeBaseEscrowV1 and RulesEngineV1.
Moved approveBuyer() call before disputeEscrow() since approvals
are only allowed in Funded state. Test now correctly verifies that
buyer cannot release from Disputed state even with prior approval.
Added explicit check that when escrow.state == Disputed, only the
mediator can call releaseToSeller() or refundToBuyer(). This prevents
buyers/sellers from bypassing dispute resolution using prior approvals.

Critical invariant: Disputed escrows require mediator intervention.
Update implementation addresses for SafeBaseEscrow and RulesEngine
following successful UUPS upgrades on Base Sepolia and Mainnet.

Base Sepolia (84532):
- SafeBaseEscrow: 0x682026827839A367252Ec80a0bbaaA47AFA3d870
- RulesEngine: 0xFA439194fe9B624AD51f7ecccf9FbcdB4350Bc70

Base Mainnet (8453):
- SafeBaseEscrow: 0xA1e13a0E7E54bC71ee4173D74773b455A86816aB
- RulesEngine: 0xB49e7b4cCB76B3aE9439798eb980434CBCF8c428

All contracts verified on Basescan.
@natalya-bbr natalya-bbr merged commit f323dc4 into main Dec 11, 2025
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant